// 
//
//	File: Material.cpp
//
// Material's implementation.
#include <stdio.h>
#include "Material.h"
#include "MaterialProperties.h"
#include "Util.h"



//    _____              
//   |  __ \             
//   | |__) |_ _ ___ ___ 
//   |  ___/ _` / __/ __|
//   | |  | (_| \__ \__ \
//   |_|   \__,_|___/___/
//                       

CMaterial::Pass::Pass() : begin_(INIT_MATERIALPROPERTY_BEGIN_NUM),
	end_(INIT_MATERIALPROPERTY_END_NUM),
	update_(INIT_MATERIALPROPERTY_UPDATE_NUM)
{
}


CMaterial::Pass::~Pass()
{
	for( int i=0; i<begin_.Count(); i++ )
	{
		if( begin_[i] )
			delete begin_[i];
		begin_[i] = NULL;
	}
}


// Clone()
CMaterial::Pass * CMaterial::Pass::Clone()
{
	Pass * newPass = new Pass();

	newPass->begin_.Resize( begin_.Count() );
	newPass->end_.Resize( end_.Count() );
	newPass->update_.Resize( update_.Count() );

	for( int i = 0; i < begin_.Count(); ++i )
	{
		MaterialProperty * property = begin_[i]->Clone();
		newPass->begin_.Push( property );
		if( property->RegisterEnd() )
			newPass->end_.Push( property );
		if( property->IsDynamic() )
			newPass->update_.Push( property );
	}

	return newPass;
}



void CMaterial::Pass::AddProperty( MaterialProperty * property )
{
	if( !begin_.Push( property ) )
	{
		begin_.Resize(begin_.Size() + INIT_MATERIALPROPERTY_BEGIN_NUM);
		begin_.Push( property );
	}
	if( property->RegisterEnd() )
	{
		if( !end_.Push( property ) )
		{
			end_.Resize(end_.Size() + INIT_MATERIALPROPERTY_END_NUM);
			end_.Push( property );
		}
	}
	if( property->IsDynamic() )
	{
		if( !update_.Push( property ) )
		{
			update_.Resize(update_.Size() + INIT_MATERIALPROPERTY_UPDATE_NUM);
			update_.Push( property );
		}
	}
}

void CMaterial::Pass::UpdateTexture(unsigned int texID, int unit)
{
	for( int i = 0; i < begin_.Count(); ++i )
		begin_[i]->UpdateTexture(texID, unit);
}


// PreRender()
void CMaterial::Pass::PreRender()
{
	for( int i = 0; i < begin_.Count(); ++i )
		begin_[i]->Begin();
}


// PostRender()
void CMaterial::Pass::PostRender()
{
	for( int i = 0; i < end_.Count(); ++i )
		end_[i]->End();
}



//    __  __       _            _       _ 
//   |  \/  |     | |          (_)     | |
//   | \  / | __ _| |_ ___ _ __ _  __ _| |
//   | |\/| |/ _` | __/ _ \ '__| | / _` | |
//   | |  | | (_| | ||  __/ |  | | (_| | |
//   |_|  |_|\__,_|\__\___|_|  |_|\__,_|_|
//                                        


// defines
#define CHECK_RESULT( op )							\
{													\
	EMaterialResult result = op;					\
	if( result == MR_SkipPass )						\
	{												\
		delete pass;								\
		pass = NULL;								\
		continue;									\
	}												\
}


// ctor
CMaterial::CMaterial() :
	AbstractMaterial()
{
	passes_.Push( new Pass );
}


// ctor
CMaterial::CMaterial( char* MaterialStr ) :
	AbstractMaterial()
{
	LoadFromMaterialStr( MaterialStr );
}


// dtor
CMaterial::~CMaterial()
{
	Clear();
}


// Clear()
void CMaterial::Clear()
{
	for( int i = 0; i < passes_.Count(); ++i )
	{
		if( passes_[i] )
			delete passes_[i];
		passes_[i] = NULL;
	}

	passes_.Clear();
	SetDynamic( false );
	transparent_ = false;
}

void CMaterial::Clone( AbstractMaterial *pSrcMaterial )
{
	if( !pSrcMaterial )
		return;

	Clear();

	transparent_ = pSrcMaterial->IsTransparent();
	mergeMode_ = pSrcMaterial->GetMergeMode();

	passes_.Resize( pSrcMaterial->GetPassCount() );

	for( int j=0; j<pSrcMaterial->GetPassCount(); ++j )
		passes_.Push( ( static_cast<Pass*>(pSrcMaterial->GetRenderPass(j)) )->Clone() );
}


// LoadFromMaterialStr()
void CMaterial::LoadFromMaterialStr( char* str )
{
	Clear();

	Script script( str );

	// modifier merge mode
	const char * mergeMode = script.GetString( "merge_mode", "add" );
	if( strcmp( mergeMode, "add" ) == 0 )
		mergeMode_ = MM_Add;
	else if( strcmp( mergeMode, "blend" ) == 0 )
		mergeMode_ = MM_Blend;
	else if( strcmp( mergeMode, "replace" ) == 0 )
		mergeMode_ = MM_Replace;

	// loop through material passes
	int numPasses = script.ContainSection("pass");
	for( int i = 0; i < numPasses; ++i )
	{
		Script::Section *pScriptPass = script.GetSection("pass", i);

		// create the pass
		Pass * pass = new Pass;

		// setup the pass
		CHECK_RESULT( SetupTexturing( script, *pScriptPass, *pass ) );
		CHECK_RESULT( SetupTexturStageState( script, *pScriptPass, *pass ) );
		//CHECK_RESULT( SetupColoring( script, *pScriptPass, *pass ) );
		CHECK_RESULT( SetupAlphaBlending( script, *pScriptPass, *pass ) );
		CHECK_RESULT( SetupAlphaTesting( script, *pScriptPass, *pass ) );
		CHECK_RESULT( SetupStencil( script, *pScriptPass, *pass ) );
		CHECK_RESULT( SetupVarious( script, *pScriptPass, *pass ) );

		// push the pass
		if( !passes_.Push( pass ) )
		{
			passes_.Resize(passes_.Size() + INIT_RENDERPASS_ARRAY_NUM);
			passes_.Push( pass );
		}

		// the material is dynamic if at least one of its pass contains at least one dynamic property
		SetDynamic( IsDynamic() || !(pass->update_.Count()==0) );
	}

	// transparent flag
	transparent_ = script.GetBool( "transparent", transparent_ );
}


// Update()
void CMaterial::Update( float elapsedTime )
{
	for( int i=0; i<passes_.Count(); ++i )
	{
		Pass * pass = static_cast<Pass *>( passes_[i] );
		for( int j=0; j<pass->update_.Count(); ++j )
			pass->update_[j]->Update(elapsedTime);
	}
}


// SetupTexturing()
CMaterial::EMaterialResult CMaterial::SetupTexturing( const Script & script, Script::Section & scriptPass, Pass & pass )
{
	int textureSectionNum = scriptPass.ContainSection( "texture" );

	if( textureSectionNum == 0 )
	{
		// disable texture unit 0 for this pass if merge mode is not MM_Blend
		if( mergeMode_ != MM_Blend )
			pass.AddProperty( new DisableTextureProperty( 0 ) );
			
		return MR_AllRight;
	}



	bool bSetUint0Texture = false;

	// texture unit 0 is enabled by default
	// texture on all other units is disabled by default
	
	for( int unit = 0; unit < textureSectionNum; ++unit )
	{
		Script::Section * scriptTexture = scriptPass.GetSection( "texture", unit );

		// set the texture property
		if( scriptTexture->ContainString( "type" ) )
		{
			const char* type_name = scriptTexture->GetString( "type" );
			if( _strnicmp( "diffuse", type_name, 7 ) == 0 )
			{
				////const string filename_diffuse = this->DiffuseTextureName;
				//TextureD3DProperty *pD3D9TextureProperty = new TextureD3DProperty( UsedD3D9Texture[0], 0, scriptTexture->GetString( "shader_name" ) );
				//pass.AddProperty( pD3D9TextureProperty );
				//pass.d3d9texture_.push_back( pD3D9TextureProperty );
				bSetUint0Texture = true;
			}	
			//else if ( type_name == "shadow" )
			//{
			//	//const string filename_shadow = this->ShadowMapTextureName;
			//	TextureD3DProperty *pD3D9TextureProperty = new TextureD3DProperty( UsedD3D9Texture[1], 1, scriptTexture->GetString( "shader_name" ) );
			//	pass.AddProperty( pD3D9TextureProperty );
			//	pass.d3d9texture_.push_back( pD3D9TextureProperty );
			//}					
		}

		//set the texture Sampler states property
		if( scriptTexture->ContainString( "addressu" ) )
		{
			const char* address_u = scriptTexture->GetString( "addressu" );
			pass.AddProperty( new SamplerStateProperty( unit, D3DSAMP_ADDRESSU, TextureAddressingModes(address_u, script) ) );
		}
		if( scriptTexture->ContainString( "addressv" ) )
		{
			const char* address_v = scriptTexture->GetString( "addressv" );
			pass.AddProperty( new SamplerStateProperty( unit, D3DSAMP_ADDRESSV, TextureAddressingModes(address_v, script) ) );
		}
		if( scriptTexture->ContainString( "addressw" ) )
		{
			const char* address_w = scriptTexture->GetString( "addressw" );
			pass.AddProperty( new SamplerStateProperty( unit, D3DSAMP_ADDRESSW, TextureAddressingModes(address_w, script) ) );
		}
		if( scriptTexture->ContainString( "bordercolor" ) )
		{
			const OyVector4D border_color = scriptTexture->GetVec4( "bordercolor" );
			pass.AddProperty( new SamplerStateProperty( unit, D3DSAMP_BORDERCOLOR, D3DCOLOR_COLORVALUE(border_color.x, border_color.y, border_color.z, border_color.w) ) );
		}
		if( scriptTexture->ContainString( "magfilter" ) )
		{
			const char* mag_filter = scriptTexture->GetString( "magfilter" );
			pass.AddProperty( new SamplerStateProperty( unit, D3DSAMP_MAGFILTER, TextureFilteringModes(mag_filter, script) ) );
		}
		if( scriptTexture->ContainString( "minfilter" ) )
		{
			const char* min_filter = scriptTexture->GetString( "minfilter" );
			pass.AddProperty( new SamplerStateProperty( unit, D3DSAMP_MINFILTER, TextureFilteringModes(min_filter, script) ) );
		}
		if( scriptTexture->ContainString( "mipfilter" ) )
		{
			const char* mip_filter = scriptTexture->GetString( "mipfilter" );
			pass.AddProperty( new SamplerStateProperty( unit, D3DSAMP_MIPFILTER, TextureFilteringModes(mip_filter, script) ) );
		}
		if( scriptTexture->ContainString( "mipmaplodbias" ) )
		{
			const int mipmaplodbias = scriptTexture->GetInt( "mipmaplodbias" );
			pass.AddProperty( new SamplerStateProperty( unit, D3DSAMP_MIPMAPLODBIAS, (DWORD)mipmaplodbias ) );
		}
		if( scriptTexture->ContainString( "maxmiplevel" ) )
		{
			const int maxmiplevel = scriptTexture->GetInt( "maxmiplevel" );
			pass.AddProperty( new SamplerStateProperty( unit, D3DSAMP_MAXMIPLEVEL, (DWORD)maxmiplevel ) );
		}
		if( scriptTexture->ContainString( "maxanisotropy" ) )
		{
			const int maxanisotropy = scriptTexture->GetInt( "maxanisotropy" );
			pass.AddProperty( new SamplerStateProperty( unit, D3DSAMP_MAXANISOTROPY, (DWORD)maxanisotropy ) );
		}
		if( scriptTexture->ContainString( "srgbtexture" ) )
		{
			const int srgbtexture = scriptTexture->GetInt( "srgbtexture" );
			pass.AddProperty( new SamplerStateProperty( unit, D3DSAMP_SRGBTEXTURE, (DWORD)srgbtexture ) );
		}
		if( scriptTexture->ContainString( "elementindex" ) )
		{
			const int elementindex = scriptTexture->GetInt( "elementindex" );
			pass.AddProperty( new SamplerStateProperty( unit, D3DSAMP_ELEMENTINDEX, (DWORD)elementindex ) );
		}
		if( scriptTexture->ContainString( "dmapoffset" ) )
		{
			const int dmapoffset = scriptTexture->GetInt( "dmapoffset" );
			pass.AddProperty( new SamplerStateProperty( unit, D3DSAMP_DMAPOFFSET, (DWORD)dmapoffset ) );
		}		
	}

	if( !bSetUint0Texture && mergeMode_ != MM_Blend )
		pass.AddProperty( new DisableTextureProperty( 0 ) );

	return MR_AllRight;
}

// SetupTexturStageState
CMaterial::EMaterialResult CMaterial::SetupTexturStageState( const Script & script, Script::Section & scriptPass, Pass & pass )
{
	char SamplerName[16];

	if( scriptPass.ContainSection( "texture_stage_state" ) > 0 )
	{
		Script::Section *pStageSection = scriptPass.GetSection( "texture_stage_state" );

		for( int unit = 0; unit < 8; ++unit )
		{
			sprintf_s( SamplerName, "stage%d", unit );
			if( pStageSection->ContainSection(SamplerName) == 0 )
				continue;

			Script::Section * section = pStageSection->GetSection( SamplerName );
			if( section->ContainString("colorop") )
			{
				const char* colorop = section->GetString( "colorop" );
				pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_COLOROP, GetTextureOp(colorop, script) ) );
			}
			if( section->ContainString("colorarg1") )
			{
				const char* colorarg1 = section->GetString( "colorarg1" );
				pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_COLORARG1, GetTextureTa(colorarg1, script) ) );
			}
			if( section->ContainString("colorarg2") )
			{
				const char* colorarg2 = section->GetString( "colorarg2" );
				pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_COLORARG2, GetTextureTa(colorarg2, script) ) );
			}
			if( section->ContainString("alphaop") )
			{
				const char* alphaop = section->GetString( "alphaop" );
				pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_ALPHAOP, GetTextureOp(alphaop, script) ) );
			}
			if( section->ContainString("alphaarg1") )
			{
				const char* alphaarg1 = section->GetString( "alphaarg1" );
				pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_ALPHAARG1, GetTextureTa(alphaarg1, script) ) );
			}
			if( section->ContainString("alphaarg2") )
			{
				const char* alphaarg2 = section->GetString( "alphaarg2" );
				pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_ALPHAARG2, GetTextureTa(alphaarg2, script) ) );
			}
			if( section->ContainString("bumpenvmat00") )
			{
				const float bumpenvmat00 = section->GetFloat( "bumpenvmat00" );
				pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_BUMPENVMAT00, *(DWORD*)(&bumpenvmat00) ) );
			}
			if( section->ContainString("bumpenvmat01") )
			{
				const float bumpenvmat01 = section->GetFloat( "bumpenvmat01" );
				pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_BUMPENVMAT01, *(DWORD*)(&bumpenvmat01) ) );
			}
			if( section->ContainString("bumpenvmat10") )
			{
				const float bumpenvmat10 = section->GetFloat( "bumpenvmat10" );
				pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_BUMPENVMAT10, *(DWORD*)(&bumpenvmat10) ) );
			}
			if( section->ContainString("bumpenvmat11") )
			{
				const float bumpenvmat11 = section->GetFloat( "bumpenvmat11" );
				pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_BUMPENVMAT11, *(DWORD*)(&bumpenvmat11) ) );
			}

			if( section->ContainString("texcoordindex") )
			{
				const char* texcoordindex = section->GetString( "texcoordindex" );
				if( _strnicmp( "passthru", texcoordindex, 8 ) == 0 )
					pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU ) );
				else if( _strnicmp( "passthru+1", texcoordindex, 10 ) == 0 )
					pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU+1 ) );
				else if( _strnicmp( "cameraspacenormal", texcoordindex, 17 ) == 0 )
					pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACENORMAL ) );
				else if( _strnicmp( "cameraspacenormal+1", texcoordindex, 19 ) == 0 )
					pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACENORMAL | 1 ) );
				else if( _strnicmp( "cameraspaceposition", texcoordindex, 19 ) == 0 )
					pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION ) );
				else if( _strnicmp( "cameraspaceposition+1", texcoordindex, 21 ) == 0 )
					pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION | 1 ) );
				else if( _strnicmp( "cameraspacereflectionvector", texcoordindex, 27 ) == 0 )
					pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR ) );
				else if( _strnicmp( "spheremap", texcoordindex, 9 ) == 0 )
					pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_SPHEREMAP ) );
				else
				{
					int texcoord_index = atoi(texcoordindex);
					pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_TEXCOORDINDEX, texcoord_index ) );
				}
			}

			if( section->ContainString("bumpenvlscale") )
			{
				const float bumpenvlscale = section->GetFloat( "bumpenvlscale" );
				pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_BUMPENVLSCALE, *(DWORD*)(&bumpenvlscale) ) );
			}
			if( section->ContainString("bumpenvloffset") )
			{
				const float bumpenvloffset = section->GetFloat( "bumpenvloffset" );
				pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_BUMPENVLOFFSET, *(DWORD*)(&bumpenvloffset) ) );
			}
			if( section->ContainString("texturetransformflags") )
			{
				const char* texturetransformflags = section->GetString( "texturetransformflags" );
				if( _strnicmp( "disable", texturetransformflags, 7 ) == 0 )
					pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE ) );
				else if( _strnicmp( "count1", texturetransformflags, 6 ) == 0 )
					pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT1 ) );
				else if( _strnicmp( "count2", texturetransformflags, 6 ) == 0 )
					pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 ) );
				else if( _strnicmp( "count3", texturetransformflags, 6 ) == 0 )
					pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT3 ) );
				else if( _strnicmp( "count4", texturetransformflags, 6 ) == 0 )
					pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT4 ) );
				else if( _strnicmp( "projected", texturetransformflags, 9 ) == 0 )
					pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_PROJECTED ) );
				else
					assert(0);			
			}
			if( section->ContainString("colorarg0") )
			{
				const char* colorarg0 = section->GetString( "colorarg0" );
				pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_COLORARG0, GetTextureTa(colorarg0, script) ) );
			}
			if( section->ContainString("alphaarg0") )
			{
				const char* alphaarg0 = section->GetString( "alphaarg0" );
				pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_ALPHAARG0, GetTextureTa(alphaarg0, script) ) );
			}
			if( section->ContainString("resultarg") )
			{
				const char* resultarg = section->GetString( "resultarg" );
				pass.AddProperty( new TextureStageStateProperty( unit, D3DTSS_RESULTARG, GetTextureTa(resultarg, script) ) );
			}
		}
	}
	return MR_AllRight;
}


#if 0
// GetColorOperation()
ColorProperty::ColorOperation CMaterial::GetColorOperation( const char* op, const Script & script )
{
	ColorProperty::ColorOperation result = ColorProperty::CO_Replace;

	if ( scriptPass.ContainString( id ) )
	{
		const string & op = scriptPass.GetString( id );
		if ( op == "add" )
			result = ColorProperty::CO_Add;
		else if ( op == "multiply" )
			result = ColorProperty::CO_Multiply;
	}

	return result;
}


// SetupColoring()
Material::EMaterialResult Material::SetupColoring( const Script & script, const Script::Section & scriptPass, Pass & pass )
{
	if ( scriptPass.ContainVec4( "emissive" ) )
	{
		D3DXVECTOR4 color( scriptPass.GetVec4( "emissive" ) );
		ColorProperty::ColorOperation op = GetColorOperation( scriptPass, "emissive_op" );
		pass.AddProperty( new EmissiveProperty( color, op ) );
	}

	if ( scriptPass.ContainVec4( "ambient" ) )
	{
		D3DXVECTOR4 color( scriptPass.GetVec4( "ambient" ) );
		ColorProperty::ColorOperation op = GetColorOperation( scriptPass, "ambient_op" );
		pass.AddProperty( new AmbientProperty( color, op ) );
	}

	if ( scriptPass.ContainVec4( "diffuse" ) )
	{
		D3DXVECTOR4 color( scriptPass.GetVec4( "diffuse" ) );
		ColorProperty::ColorOperation op = GetColorOperation( scriptPass, "diffuse_op" );
		pass.AddProperty( new DiffuseProperty( color, op ) );
	}

	if ( scriptPass.ContainVec4( "specular" ) )
	{
		D3DXVECTOR4 color( scriptPass.GetVec4( "specular" ) );
		ColorProperty::ColorOperation op = GetColorOperation( scriptPass, "specular_op" );
		pass.AddProperty( new SpecularProperty( color, op ) );
	}

	if ( scriptPass.ContainFloat( "shininess" ) )
	{
		float value = scriptPass.GetFloat( "shininess" );
		pass.AddProperty( new ShininessProperty( value ) );
	}

	return MR_AllRight;
}
#endif



D3DCMPFUNC CMaterial::AlphaTestFunc( const char* func, const Script & script )
{
	if( _strnicmp( "always", func, 6 ) == 0 )
		return D3DCMP_ALWAYS;
	else if( _strnicmp( "never", func, 5 ) == 0 )
		return D3DCMP_NEVER;
	else if( _strnicmp( "less", func, 4 ) == 0 )
		return D3DCMP_LESS;
	else if( _strnicmp( "equal", func, 5 ) == 0 )
		return D3DCMP_EQUAL;
	else if( _strnicmp( "lessequal", func, 9 ) == 0 )
		return D3DCMP_LESSEQUAL;
	else if( _strnicmp( "notequal", func, 8 ) == 0 )
		return D3DCMP_NOTEQUAL;
	else if( _strnicmp( "greater", func, 7 ) == 0 )
		return D3DCMP_GREATER;
	else if( _strnicmp( "greaterequal", func, 12 ) == 0 )
		return D3DCMP_GREATEREQUAL;
		
	return D3DCMP_ALWAYS;	
}

DWORD CMaterial::AlphaTestRef( const char* ref, const Script & script )
{
	DWORD color_ref = 0;
	
	if( _strnicmp( "0x", ref, 2 ) == 0 )
		color_ref = CharToHex( ref );

	return color_ref;
}

D3DSTENCILOP CMaterial::StencilOp( const char* op, const Script & script )
{
	if( _strnicmp( "keep", op, 4 ) == 0 )
		return D3DSTENCILOP_KEEP;
	else if( _strnicmp( "zero", op, 4 ) == 0 )
		return D3DSTENCILOP_ZERO;
	else if( _strnicmp( "replace", op, 7 ) == 0 )
		return D3DSTENCILOP_REPLACE;
	else if( _strnicmp( "incrsat", op, 7 ) == 0 )
		return D3DSTENCILOP_INCRSAT;
	else if( _strnicmp( "decrsat", op, 7 ) == 0 )
		return D3DSTENCILOP_DECRSAT;
	else if( _strnicmp( "invert", op, 6 ) == 0 )
		return D3DSTENCILOP_INVERT;
	else if( _strnicmp( "incr", op, 4 ) == 0 )
		return D3DSTENCILOP_INCR;
	else if( _strnicmp( "decr", op, 4 ) == 0 )
		return D3DSTENCILOP_DECR;

	return D3DSTENCILOP_KEEP;
}

D3DCMPFUNC CMaterial::StencilFunc( const char* func, const Script & script )
{
	return AlphaTestFunc(func, script);
}


// AlphaBlendFunc()
D3DBLEND CMaterial::AlphaBlendFunc( const char* func, const Script & script )
{
	if( _strnicmp( "zero", func, 4 ) == 0 )
		return D3DBLEND_ZERO;
	else if( _strnicmp( "one", func, 3 ) == 0 )
		return D3DBLEND_ONE;
	else if( _strnicmp( "srccolor", func, 8 ) == 0 )
		return D3DBLEND_SRCCOLOR;
	else if( _strnicmp( "invsrccolor", func, 11 ) == 0 )
		return D3DBLEND_INVSRCCOLOR;
	else if( _strnicmp( "destcolor", func, 9 ) == 0 )
		return D3DBLEND_DESTCOLOR;
	else if( _strnicmp( "invdestcolor", func, 12 ) == 0 )
		return D3DBLEND_INVDESTCOLOR;
	else if( _strnicmp( "srcalpha", func, 8 ) == 0 )
		return D3DBLEND_SRCALPHA;
	else if( _strnicmp( "invsrcalpha", func, 11 ) == 0 )
		return D3DBLEND_INVSRCALPHA;
	else if( _strnicmp( "destalpha", func, 9 ) == 0 )
		return D3DBLEND_DESTALPHA;
	else if( _strnicmp( "invdestalpha", func, 12 ) == 0 )
		return D3DBLEND_INVDESTALPHA;
	else
		return D3DBLEND_ZERO;
}

D3DBLENDOP CMaterial::AlphaBlendOp( const char* op, const Script & script )
{
	if( _strnicmp( "add", op, 3 ) == 0 )
		return D3DBLENDOP_ADD;
	else if( _strnicmp( "subtract", op, 8 ) == 0 )
		return D3DBLENDOP_SUBTRACT;
	else if( _strnicmp( "revsubtract", op, 11 ) == 0 )
		return D3DBLENDOP_REVSUBTRACT;
	else if( _strnicmp( "min", op, 3 ) == 0 )
		return D3DBLENDOP_MIN;
	else if( _strnicmp( "max", op, 3 ) == 0 )
		return D3DBLENDOP_MAX;

	return D3DBLENDOP_ADD;

}

// SetupAlphaBlending()
CMaterial::EMaterialResult CMaterial::SetupAlphaBlending( const Script & script, Script::Section & scriptPass, Pass & pass )
{ 
	if( scriptPass.ContainSection( "alpha_blend" ) > 0 )
	{
		// retrieve the section
		Script::Section * section = scriptPass.GetSection( "alpha_blend" );
		

		// get the blending functions & add the property
		const char* op = section->GetString( "op", "add" );
		const char* src = section->GetString( "src", "srcalpha" );
		const char* dst = section->GetString( "dst", "invsrcalpha" );
		pass.AddProperty( new AlphaBlendProperty( AlphaBlendFunc( src, script ), AlphaBlendFunc( dst, script ) ) );

		if( _strnicmp( "add", op, 3 ) != 0 )
			pass.AddProperty( new AlphaBlendOpProperty( AlphaBlendOp( op, script ) ) );

		// set the material as transparent if alpha blending is enabled on the first pass
		if( passes_.Count() == 0 )
			transparent_ = true;
	}

	if( scriptPass.ContainSection( "alpha_blendalpha" ) )
	{
		// retrieve the section
		Script::Section * section = scriptPass.GetSection( "alpha_blendalpha" );

		// get the blending functions & add the property
		const char* op = section->GetString( "op", "add" );
		const char* src = section->GetString( "src", "one" );
		const char* dst = section->GetString( "dst", "zero" );
		pass.AddProperty( new AlphaBlendAlphaProperty( AlphaBlendFunc( src, script ), AlphaBlendFunc( dst, script ), AlphaBlendOp( op, script ) ) );

		// set the material as transparent if alpha blending is enabled on the first pass
		if( passes_.Count() == 0 )
			transparent_ = true;
	}

	return MR_AllRight;
}


// SetupAlphaTesting()
CMaterial::EMaterialResult CMaterial::SetupAlphaTesting( const Script & script, Script::Section & scriptPass, Pass & pass )
{
	if( scriptPass.ContainSection( "alpha_test" ) > 0 )
	{
		Script::Section * section = scriptPass.GetSection( "alpha_test" );
		
		// get the blending functions & add the property
		const char* func = section->GetString( "func", "always" );
		const char* ref = section->GetString( "ref", "0" );
		
		pass.AddProperty( new AlphaTestFuncProperty( AlphaTestFunc( func, script ) ) );
		pass.AddProperty( new AlphaTestRefProperty( AlphaTestRef( ref, script ) ) );
	}
	return MR_AllRight;
}

// SetupStencil()
CMaterial::EMaterialResult CMaterial::SetupStencil( const Script & script, Script::Section & scriptPass, Pass & pass )
{
	if( scriptPass.ContainSection( "stencil_enable" ) > 0 )
	{
		// retrieve the section
		Script::Section * section = scriptPass.GetSection( "stencil_enable" );
		
		if( section->GetBool( "enable", false ) )
			pass.AddProperty( new EnableStateProperty( D3DRS_STENCILENABLE ) );
		else
		{
			pass.AddProperty( new DisableStateProperty( D3DRS_STENCILENABLE ) );
			return MR_AllRight;
		}

		const char* func = section->GetString( "func", "always" );
		const char* fail = section->GetString( "fail", "keep" );
		const char* zfail = section->GetString( "zfail", "keep" );
		const char* spass = section->GetString( "pass", "keep" );
		const char* ref = section->GetString( "ref", "0" );
		const char* mask = section->GetString( "mask", "0xFFFFFFFF" );
		const char* writemask = section->GetString( "writemask", "0xFFFFFFFF" );

		int sref = atoi(ref);
		DWORD smask = CharToHex(mask);
		DWORD swritemask = CharToHex(writemask);


		pass.AddProperty( 
			new StencilProperty( 
				StencilFunc(func, script),
				StencilOp(fail, script),
				StencilOp(zfail, script),
				StencilOp(spass, script),
				sref,
				smask,
				swritemask ) );

	}
	return MR_AllRight;
}


// SetupVarious()
CMaterial::EMaterialResult CMaterial::SetupVarious( const Script & script, Script::Section & scriptPass, Pass & pass )
{
	if( scriptPass.ContainString( "cull_mode" ) )
	{
		const char* cullMode = scriptPass.GetString( "cull_mode" );
		if( _strnicmp( "none", cullMode, 4 ) == 0 )
			pass.AddProperty( new CullingModeProperty( D3DCULL_NONE ) );
		else if( _strnicmp( "ccw", cullMode, 5 ) == 0 )
			pass.AddProperty( new CullingModeProperty(D3DCULL_CCW) );
		else if( _strnicmp( "cw", cullMode, 4 ) == 0 )
			pass.AddProperty( new CullingModeProperty(D3DCULL_CW) );
		else
			assert(0);
	}
	
	if( scriptPass.ContainString( "color_write" ) )
	{
		const char* color_writeable = scriptPass.GetString( "color_write" );
		if( _strnicmp( "rgba", color_writeable, 4 ) == 0 )
			pass.AddProperty( new ColorMaskProperty( D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED ) );
		else if( _strnicmp( "rgb", color_writeable, 3 ) == 0 )
			pass.AddProperty( new ColorMaskProperty( D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED ) );
		else if( _strnicmp( "rb", color_writeable, 2 ) == 0 )
			pass.AddProperty( new ColorMaskProperty( D3DCOLORWRITEENABLE_RED  | D3DCOLORWRITEENABLE_BLUE ) );
		else if( _strnicmp( "rba", color_writeable, 3 ) == 0 )
			pass.AddProperty( new ColorMaskProperty( D3DCOLORWRITEENABLE_RED  | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA ) );
		else if( *color_writeable == 'a' || *color_writeable == 'A' )
			pass.AddProperty( new ColorMaskProperty( D3DCOLORWRITEENABLE_ALPHA ) );
		else if( *color_writeable == 'r' || *color_writeable == 'R' )
			pass.AddProperty( new ColorMaskProperty( D3DCOLORWRITEENABLE_RED ) );	
		else if( *color_writeable == 'g' || *color_writeable == 'G' )
			pass.AddProperty( new ColorMaskProperty( D3DCOLORWRITEENABLE_GREEN ) );	
		else if( *color_writeable == 'b' || *color_writeable == 'B' )
			pass.AddProperty( new ColorMaskProperty( D3DCOLORWRITEENABLE_BLUE ) );
		else if( _strnicmp( "none", color_writeable, 4 ) == 0 )
			pass.AddProperty( new ColorMaskProperty( 0 ) );
		else 
			assert(0);
	}

	//if( scriptPass.ContainString( "fogtable_mode" ) )
	//{
	//	const char* fogtablemode = scriptPass.GetString( "fogtable_mode" );
	//	if( _strnicmp( "none", fogtablemode, 4 ) == 0 )
	//		pass.AddProperty( new FogTableModeProperty( D3DFOG_NONE ) );
	//	else if( _strnicmp( "exp", fogtablemode, 3 ) == 0 )
	//		pass.AddProperty( new FogTableModeProperty( D3DFOG_EXP ) );
	//	else if( _strnicmp( "exp2", fogtablemode, 4 ) == 0 )
	//		pass.AddProperty( new FogTableModeProperty( D3DFOG_EXP2 ) );
	//	else if( _strnicmp( "linear", fogtablemode, 6 ) == 0 )
	//		pass.AddProperty( new FogTableModeProperty( D3DFOG_LINEAR ) );
	//	else
	//		assert(0);
	//}

	//if( scriptPass.ContainString( "fogvertex_mode" ) )
	//{
	//	const char* fogvertexmode = scriptPass.GetString( "fogvertex_mode" );
	//	if ( _strnicmp( "none", fogvertexmode, 4 ) == 0 )
	//		pass.AddProperty( new FogVertexModeProperty( D3DFOG_NONE ) );
	//	else if ( _strnicmp( "exp", fogvertexmode, 3 ) == 0 )
	//		pass.AddProperty( new FogVertexModeProperty( D3DFOG_EXP ) );
	//	else if ( _strnicmp( "exp2", fogvertexmode, 4 ) == 0 )
	//		pass.AddProperty( new FogVertexModeProperty( D3DFOG_EXP2 ) );
	//	else if ( _strnicmp( "linear", fogvertexmode, 6 ) == 0 )
	//		pass.AddProperty( new FogVertexModeProperty( D3DFOG_LINEAR ) );
	//	else
	//		assert(0);
	//}

	if( scriptPass.ContainString( "texture_factor" ) )
	{
		const char* tex_factor = scriptPass.GetString( "texture_factor" );
		DWORD TextureFactor = CharToHex(tex_factor);
		
		pass.AddProperty( new TextureFactorProperty( TextureFactor ) );
	}




	if( !scriptPass.GetBool( "depth_test", true ) )
		pass.AddProperty( new DepthEnableProperty( D3DZB_FALSE ) );		

	if( !scriptPass.GetBool( "depth_write", true ) )
		pass.AddProperty( new DisableStateProperty( D3DRS_ZWRITEENABLE ) );

	if( !scriptPass.GetBool( "vertex_color", true ) )
		pass.AddProperty( new DisableStateProperty( D3DRS_COLORVERTEX ) );

	/////////////////////////////////////////////////////////////////////////////

	if( scriptPass.ContainString( "depth_bias" ) )
		pass.AddProperty( new DepthBiasProperty( scriptPass.GetFloat( "depth_bias" ) ) );

	if( !scriptPass.GetBool( "lighting", true ) )
		pass.AddProperty( new LightingDisableProperty( TRUE ) );

	if( scriptPass.GetBool( "depth_greater", false ) )
		pass.AddProperty( new DepthFuncProperty( D3DCMP_GREATER ) );

	if( scriptPass.GetBool( "line_drawing", false ) )
		pass.AddProperty( new LineDrawingProperty() );

	if( scriptPass.GetBool( "specular_enable", false ) )
		pass.AddProperty( new EnableStateProperty( D3DRS_SPECULARENABLE ) );

	if( scriptPass.GetBool( "fog_enable", false ) )
		pass.AddProperty( new EnableStateProperty( D3DRS_FOGENABLE ) );

	return MR_AllRight;
}

D3DTEXTUREADDRESS CMaterial::TextureAddressingModes( const char* mode, const Script & script )
{
	if( _strnicmp( "wrap", mode, 4 ) == 0 )
		return D3DTADDRESS_WRAP;
	else if( _strnicmp( "mirror", mode, 6 ) == 0 )
		return D3DTADDRESS_MIRROR;
	else if( _strnicmp( "clamp", mode, 5 ) == 0 )
		return D3DTADDRESS_CLAMP;
	else if( _strnicmp( "border", mode, 6 ) == 0 )
		return D3DTADDRESS_BORDER;
	else if( _strnicmp( "mirroronce", mode, 10 ) == 0 )
		return D3DTADDRESS_MIRRORONCE;

	return D3DTADDRESS_WRAP;
}

D3DTEXTUREFILTERTYPE CMaterial::TextureFilteringModes( const char* mode, const Script & script )
{
	if( _strnicmp( "none", mode, 4 ) == 0 )
		return D3DTEXF_NONE;
	else if( _strnicmp( "point", mode, 5 ) == 0 )
		return D3DTEXF_POINT;
	else if( _strnicmp( "linear", mode, 6 ) == 0 )
		return D3DTEXF_LINEAR;
	else if( _strnicmp( "anisotropic", mode, 11 ) == 0 )
		return D3DTEXF_ANISOTROPIC;
	else if( _strnicmp( "pyramidalquad", mode, 13 ) == 0 )
		return D3DTEXF_PYRAMIDALQUAD;
	else if( _strnicmp( "gaussianquad", mode, 12 ) == 0 )
		return D3DTEXF_GAUSSIANQUAD;
	else if( _strnicmp( "convolutionmono", mode, 15 ) == 0 )
		return D3DTEXF_CONVOLUTIONMONO;
	return D3DTEXF_NONE;
}


D3DTEXTUREOP CMaterial::GetTextureOp( const char* op, const Script & script )
{
	if( _strnicmp( "disable", op, 7 ) == 0 )
		return D3DTOP_DISABLE;
	else if( _strnicmp( "selectarg1", op, 10 ) == 0 )
		return D3DTOP_SELECTARG1;
	else if( _strnicmp( "selectarg2", op, 10 ) == 0 )
		return D3DTOP_SELECTARG2;
	else if( _strnicmp( "modulate", op, 8 ) == 0 )
		return D3DTOP_MODULATE;
	else if( _strnicmp( "modulate2x", op, 10 ) == 0 )
		return D3DTOP_MODULATE2X;
	else if( _strnicmp( "modulate4x", op, 10 ) == 0 )
		return D3DTOP_MODULATE4X;
	else if( _strnicmp( "add", op, 3 ) == 0 )
		return D3DTOP_ADD;
	else if( _strnicmp( "addsigned", op, 9 ) == 0 )
		return D3DTOP_ADDSIGNED;
	else if( _strnicmp( "addsigned2x", op, 11 ) == 0 )
		return D3DTOP_ADDSIGNED2X;
	else if( _strnicmp( "subtract", op, 8 ) == 0 )
		return D3DTOP_SUBTRACT;
	else if( _strnicmp( "addsmooth", op, 9 ) == 0 )
		return D3DTOP_ADDSMOOTH;
	else if( _strnicmp( "blenddiffusealpha", op, 17 ) == 0 )
		return D3DTOP_BLENDDIFFUSEALPHA;
	else if( _strnicmp( "blendtexturealpha", op, 17 ) == 0 )
		return D3DTOP_BLENDTEXTUREALPHA;	
	else if( _strnicmp( "blendfactoralpha", op, 16 ) == 0 )
		return D3DTOP_BLENDFACTORALPHA;
	else if( _strnicmp( "blendtexturealphapm", op, 19 ) == 0 )
		return D3DTOP_BLENDTEXTUREALPHAPM;
	else if( _strnicmp( "blendcurrentalpha", op, 17 ) == 0 )
		return D3DTOP_BLENDCURRENTALPHA;
	else if( _strnicmp( "premodulate", op, 11 ) == 0 )
		return D3DTOP_PREMODULATE;
	else if( _strnicmp( "modulatealpha_addcolor", op, 22 ) == 0 )
		return D3DTOP_MODULATEALPHA_ADDCOLOR;
	else if( _strnicmp( "modulatecolor_addalpha", op, 22 ) == 0 )
		return D3DTOP_MODULATECOLOR_ADDALPHA;
	else if( _strnicmp( "modulateinvalpha_addcolor", op, 25 ) == 0 )
		return D3DTOP_MODULATEINVALPHA_ADDCOLOR;
	else if( _strnicmp( "modulateinvcolor_addalpha", op, 25 ) == 0 )
		return D3DTOP_MODULATEINVCOLOR_ADDALPHA;
	else if( _strnicmp( "bumpenvmap", op, 10 ) == 0 )
		return D3DTOP_BUMPENVMAP;
	else if( _strnicmp( "bumpenvmapluminance", op, 19 ) == 0 )
		return D3DTOP_BUMPENVMAPLUMINANCE;
	else if( _strnicmp( "dotproduct3", op, 11 ) == 0 )
		return D3DTOP_DOTPRODUCT3;
	else if( _strnicmp( "multiplyadd", op, 11 ) == 0 )
		return D3DTOP_MULTIPLYADD;
	else if( _strnicmp( "lerp", op, 4 ) == 0 )
		return D3DTOP_LERP;

	return D3DTOP_DISABLE;
}


DWORD CMaterial::GetTextureTa( const char* ta, const Script & script )
{
	if( _strnicmp( "constant", ta, 8 ) == 0 )
		return D3DTA_CONSTANT;
	else if( _strnicmp( "current", ta, 7 ) == 0 )
		return D3DTA_CURRENT;
	else if( _strnicmp( "diffuse", ta, 7 ) == 0 )
		return D3DTA_DIFFUSE;
	else if( _strnicmp( "selectmask", ta, 10 ) == 0 )
		return D3DTA_SELECTMASK;
	else if( _strnicmp( "specular", ta, 8 ) == 0 )
		return D3DTA_SPECULAR;
	else if( _strnicmp( "temp", ta, 4 ) == 0 )
		return D3DTA_TEMP;
	else if( _strnicmp( "texture", ta, 7 ) == 0 )
		return D3DTA_TEXTURE;
	else if( _strnicmp( "tfactor", ta, 7 ) == 0 )
		return D3DTA_TFACTOR;
	else if( _strnicmp( "alphareplicate", ta, 14 ) == 0 )
		return D3DTA_ALPHAREPLICATE;
	else if( _strnicmp( "complement", ta, 10 ) == 0 )
		return D3DTA_COMPLEMENT;

	return D3DTA_CURRENT;
}
